/**
 * 数据处理工具
 * @author luguoxiang
 * @date 2021-09-07
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
// @ts-ignore
import SparkMd5 from 'spark-md5';

/**
 * 转换为布尔值
 * @param value
 */
export function toBoolean(value: any): boolean {
  if (typeof value === 'boolean') {
    return value;
  }
  if (value === 'false') {
    return false;
  }
  if (value === 'true') {
    return true;
  }
  return !!value;
}

/**
 * 转换为数值
 * @param value
 */
export function toNumber(value: any): number {
  if (typeof value === 'number') {
    return value;
  }
  if (isNaN(value)) {
    throw new Error(`不能转换 ${value} 为数值`);
  }
  return Number(value);
}

/**
 * 转换为数值数组
 * @param value
 * @param separator 分隔符
 */
export function toNumberArray(value: any, separator = ','): number[] {
  if (typeof value === 'string') {
    value = value.split(separator);
  }
  if (!Array.isArray(value)) {
    value = [value];
  }
  return value.map(toNumber);
}

/**
 * 转换为字符串数组
 * @param value
 * @param separator 分隔符
 */
export function toStringArray(value: any, separator = ','): string[] {
  if (typeof value === 'string') {
    return value.split(separator);
  }
  if (!Array.isArray(value)) {
    value = [value];
  }
  return value.map(String);
}

/**
 * 转换为数组
 * @param value
 */
export function toArray(value: any): string[] {
  if (Array.isArray(value)) {
    return value;
  }
  return [value];
}

/**
 * 转换为 Promise
 * @param value
 */
export function toPromise(value: any): Promise<any> {
  if (value instanceof Promise) {
    return value;
  }
  return Promise.resolve(value);
}

interface Option {
  value: any;
  label: string;
  [propName: string]: any;
}
/**
 * 转换数据为 { value: any, label: string }[] 数据
 * @param data
 */
export function toOptions(data: object | string): Option[] {
  const options: Option[] = [];
  if (!data) {
    return options;
  }
  if (typeof data === 'string') {
    data = data.split(',');
  }
  if (Array.isArray(data)) {
    for (let i = 0; i < data.length; i++) {
      // @ts-ignore
      const item: any = data[i];
      // 对象参数
      if (typeof item === 'object') {
        options.push(item);
        continue;
      }
      // 字符串参数
      if (typeof item === 'string') {
        const index: number = item.indexOf(':');
        if (index !== -1) {
          // 字符串参数，使用“:”前面作为值
          options.push({ value: item.substring(0, index), label: item.substring(index + 1) });
          continue;
        }
      }
      // 字符串参数，索引作为值
      options.push({ value: i, label: item });
    }
  } else {
    // 对象参数，key作为值，value做label
    for (const v in data) {
      // @ts-ignore
      options.push({ value: v, label: data[v] });
    }
  }
  return options;
}

/**
 * 数据转为{ [string]: string }
 * @param data
 */
export function toLabelMap(data: object | string) {
  if (!data) {
    return {};
  }
  if (typeof data === 'string') {
    data = data.split(',').map((x: string) => {
      const index: number = x.indexOf(':');
      if (index === -1) {
        return x;
      }
      return { value: x.substring(0, index), label: x.substring(index + 1) };
    });
  }
  if (Array.isArray(data)) {
    const map: any = {};
    for (const i in data) {
      const item = data[i];
      if (typeof item === 'string') {
        const index: number = item.indexOf(':');
        if (index === -1) {
          map[i] = item;
        } else {
          map[item.substring(0, index)] = item.substring(index + 1);
        }
      } else if (typeof item === 'object') {
        map[item.value] = item.label;
      } else {
        map[i] = item;
      }
    }
    return map;
  }
  return data;
}

export type Getter = string | ((d: any) => any);
/**
 * 字段转get方法
 * @param field
 */
export function toGetter(field: Getter) {
  if (typeof field === 'function') {
    return field;
  }
  return (d: any) => d[field];
}

export type Setter = string | ((d: any, value: any) => void);
/**
 * 字段转set方法
 * @param field
 */
export function toSetter(field: Setter) {
  if (typeof field === 'function') {
    return field;
  }
  return (d: any, value: any) => {
    d[field] = value;
  };
}

/**
 * 生成排序器
 * @param sorter 排序方式
 */
export function toSorter(sorter: string | string[] | Function): Function {
  if (typeof sorter === 'function') {
    return sorter;
  }
  // 生成排序器
  const ls: Function[] = toStringArray(sorter).map((x) => {
    if (x.indexOf('-') === 0) {
      x = x.substring(1);
      return (a: any, b: any) => b[x] - a[x];
    }
    return (a: any, b: any) => a[x] - b[x];
  });
  // 完成排序
  return (a: any, b: any): number => {
    let result: number;
    for (const _sorter of ls) {
      result = _sorter(a, b);
      if (result !== 0) {
        return result;
      }
    }
    return 0;
  };
}

/**
 * 数组转map数据
 * @param list
 * @param key
 * @param value
 */
export function toMap(list: any[], key: string | Function, value?: string | Function) {
  const getKey = typeof key === 'string' ? (x: any) => x[key] : key;
  const getValue = value ? (typeof value === 'string' ? (x: any) => x[value] : value) : (x: any) => x;
  const map: any = {};
  for (let i = 0; i < list.length; i++) {
    const x = list[i];
    map[getKey(x, i)] = getValue(x, i);
  }
  return map;
}

/**
 * 生成md5值
 * @param data
 */
export function toMd5(data: any): string {
  const spark = new SparkMd5();
  spark.append(data);
  return spark.end();
}

/**
 * 数组转树形数据
 * @param {Array} data 源数据
 * @param {String|function} idKey    id字段
 * @param {String|function} pidKey   父id字段
 * @param {String|function} childrenKey   子级字段
 * @returns {Array}  分组后
 */
export function toTreeData(
  data: any[],
  idKey: Getter = 'id',
  pidKey: Getter = 'pId',
  childrenKey: Setter = 'children',
) {
  const getId = toGetter(idKey);
  const getPid = toGetter(pidKey);
  const setChildren = toSetter(childrenKey);

  const map: { [k: string]: any[] } = {};
  for (const x of data) {
    const pid = getPid(x);
    if (pid in map) {
      map[pid].push(x);
    } else {
      map[pid] = [x];
    }
  }
  for (const x of data) {
    const id = getId(x);
    setChildren(x, map[id]);
    delete map[id];
  }

  let res: any[] = [];
  for (const k in map) {
    res = res.concat(map[k]);
  }
  return res;

  // 递归法
  /* function findParent(parent: any) {
    const pid = parent && getId(parent);
    const sameparent: any[] = [];

    for (const a of data) {
      const apid: any = getPid(a);
      if ((!apid && !pid) || apid === pid) {
        const children = findParent(a);
        if (children.length > 0) {
          a[childrenKey] = children;
        }
        a.parent = parent;
        sameparent.push(a);
      }
    }
    return sameparent;
  }
  return findParent({}); */
}

/**
 * 遍历树形数据
 * @param {Array} list 源数据
 * @param {Function} consumer 回调方法
 * @param {String|function} childrenKey 子数组字段
 * @returns {Array}  分组后
 */
export function forEach(list: any[], consumer: Function, childrenKey: Getter = 'children') {
  const getChildren = toGetter(childrenKey);
  const rs: any[] = [];
  function call(ls: any[], parent: any) {
    if (!Array.isArray(ls)) {
      return;
    }
    for (const x of ls) {
      if (consumer(x, parent)) {
        rs.push(x);
      }
      call(getChildren(x), x);
    }
  }
  call(list, {});
  return rs;
}

/**
 * 转换
 * @param list
 * @param converter
 * @param childrenKey
 * @returns {[]}
 */
export function map(list: any[], converter: Function, childrenKey = 'children') {
  function call(ls: any[], newParent: any, oldParent: any) {
    if (!Array.isArray(ls)) {
      return ls;
    }
    const rs = [];
    for (const x of ls) {
      const data = converter(x, newParent, oldParent);
      data[childrenKey] = call(x[childrenKey], data, x);
      rs.push(data);
    }
    return rs;
  }
  return call(list, {}, {});
}

/**
 * 过滤
 * @param list
 * @param predicate
 * @param childrenKey
 * @returns {[]}
 */
export function filter(list: any[], predicate: Function, childrenKey = 'children') {
  function call(ls: any[], parent: any) {
    if (!Array.isArray(ls)) {
      return ls;
    }
    const rs = [];
    for (const x of ls) {
      const t = { ...x };
      t[childrenKey] = call(t[childrenKey], t);
      if (predicate(t, parent)) {
        rs.push(t);
      }
    }
    return rs;
  }
  return call(list, {});
}

/**
 * 搜索（符合条件的数据返回数据和数据上下级）
 * @param tree 树数据
 * @param predicate 搜索条件
 * @param childrenKey 子级字段
 */
export function query(tree: any[], predicate: Function, childrenKey = 'children') {
  function call(data: any) {
    if (!Array.isArray(data)) {
      return [];
    }
    const res = [];
    for (const t of data) {
      if (predicate(t)) {
        res.push(t);
        continue;
      }
      let children = t[childrenKey];
      if (children) {
        children = call(children);
        if (children.length) {
          t[childrenKey] = children;
          res.push(t);
        }
      }
    }
    return res;
  }
  return call(tree);
}

/**
 * 排序
 * @param list
 * @param sorter
 * @param {string|function}childrenKey
 */
export function sort(list: any[], sorter: string | string[] | Function, childrenKey?: Getter) {
  // eslint-disable-next-line no-underscore-dangle
  const _sorter = toSorter(sorter);
  if (!childrenKey) {
    // @ts-ignore
    list.sort(_sorter);
    return;
  }
  // 树形数据处理
  const getChildren = toGetter(childrenKey);
  function call(ls: any[]) {
    if (!Array.isArray(ls)) {
      return;
    }
    // @ts-ignore
    ls.sort(_sorter);
    for (const x of ls) {
      call(getChildren(x));
    }
  }
  call(list);
}

/**
 * 数租去重
 * @param {Array} list 数据源
 * @param fields 去重对比字段(不设置则使用json比对)
 * @returns {Array}
 */
export function distinct<T>(list: T[], ...fields: (keyof T)[] | [(x: T) => string]): T[] {
  if (!Array.isArray(list)) {
    return list;
  }
  let genKey: (x: T) => string;
  if (fields.length === 0) {
    genKey = (e: T) => JSON.stringify(e);
  } else if (typeof fields[0] === 'function') {
    genKey = fields[0] as (x: T) => string;
  } else {
    genKey = (e: T) => {
      let key = '';
      for (const k of fields) {
        // @ts-ignore
        key += `${e[k]}:`;
      }
      return key;
    };
  }
  const map: any = {};
  const result: T[] = [];
  for (const e of list) {
    const key = genKey(e);
    if (map[key]) {
      continue;
    }
    result.push(e);
    map[key] = true;
  }
  return result;
}

/**
 * 深拷贝
 * @param data 数据
 * @param functional 是否复制函数
 */
export function deepCopy(data: any, functional?: boolean): any {
  if (functional) {
    function handler(d: any) {
      if (d instanceof Date) {
        return new Date(d);
      }
      if (Array.isArray(d)) {
        const result: any[] = [];
        for (const x in d) {
          result[x] = handler(d[x]);
        }
        return result;
      }
      if (typeof d === 'object') {
        const result: any = {};
        for (const x in d) {
          result[x] = handler(d[x]);
        }
        return result;
      }
      return d;
    }
    return handler(data);
  }
  return JSON.parse(JSON.stringify(data));
}

/**
 * 深度合并
 * @param a 这里的属性会被b的相同属性覆盖
 * @param b 这里的所有属性保留
 */
export function deepMerge(a: any, b: any) {
  if (b && a) {
    for (const k in b) {
      const xb = b[k];
      if (!xb) {
        a[k] = xb;
      }
      if (xb instanceof Date) {
        a[k] = new Date(xb);
        continue;
      }
      if (Array.isArray(xb)) {
        a[k] = [];
        deepMerge(a[k], xb);
        continue;
      }
      if (typeof xb === 'object') {
        if (!a[k] || typeof a[k] !== 'object') {
          a[k] = {};
        }
        deepMerge(a[k], xb);
        continue;
      }
      a[k] = xb;
    }
  }
}

const Func = Function;
/**
 * json解析
 * @param jsonStr
 */
export function parseJson(jsonStr: string) {
  return new Func(`return ${jsonStr}`)();
}

/**
 * 是否符合json语法
 * @param data
 */
export function isJson(data: string) {
  try {
    parseJson(data);
    return true;
  } catch (e) {
    return false;
  }
}

/**
 * 数组删除（支持删除多个符合条件的元素）
 * @param list 数组
 * @param predicate 判断函数 或 元素值
 */
export function splice(list: any[], predicate: ((x: any, i: number) => boolean) | any) {
  const func = typeof predicate === 'function' ? predicate : (x: any) => x === predicate;
  for (let i = list.length - 1; i > -1; i--) {
    const x = list[i];
    if (func(x, i)) {
      list.splice(i, 1);
    }
  }
}

/**
 * 解析url参数
 * @param url
 */
export function parseUrlParams(url: string) {
  const urlParams = new URLSearchParams(url);
  const paramsObj: any = {};
  // @ts-ignore
  for (const [key, value] of urlParams) {
    paramsObj[key] = value;
  }
  return paramsObj;
}
